Set up
# load packages ----
library(shiny) # for shiny app
# for edit data set
library(tidyverse)
library(dplyr)
library(data.table)
# for map_plot & bar_plot
library(sf)
library(leaflet)
library(ggplot2)
# import dataset ----
setwd("/Users/zhangyudan/Documents/Github/fall2022-project2-group12/data")
electric <- read.csv('Electric_Consumption_And_Cost__2010_-_Feb_2022_.csv')
water <- read.csv('Water_Consumption_And_Cost__2013_-_Feb_2022_.csv')
gas <- read.csv('Heating_Gas_Consumption_And_Cost__2010_-__Feb_2022_.csv')
covid <- read.csv('cases-by-day.csv')
data <- st_read("Borough_Boundaries/geo_export_a59a7c07-bd70-4b11-af9f-011b5ad2a963.shp")
Reading layer `geo_export_a59a7c07-bd70-4b11-af9f-011b5ad2a963' from data source
`/Users/zhangyudan/Documents/Github/fall2022-project2-group12/data/Borough_Boundaries/geo_export_a59a7c07-bd70-4b11-af9f-011b5ad2a963.shp'
using driver `ESRI Shapefile'
Simple feature collection with 5 features and 4 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -74.25559 ymin: 40.49612 xmax: -73.70001 ymax: 40.91553
Geodetic CRS: WGS84(DD)
Clean & Edit
Energy dataset
# select cols ----
electric <- electric[,c('Borough','Revenue.Month','Consumption..KWH.')]
water <- water[,c('Borough','Revenue.Month','Consumption..HCF.')]
gas <- gas[,c('Borough','Revenue.Month','Consumption..Therms.')]
# filter data by date ----
electric <- subset(electric, Revenue.Month > 2019)
water <- subset(water, Revenue.Month > 2019)
gas <- subset(gas, Revenue.Month > 2019)
# group by Borough & date to get mean(Consumption..KWH.) ----
electric_groupby <- electric %>%
group_by(Borough,Revenue.Month) %>%
summarise_at(vars(Consumption..KWH.), list(name = mean))
water_groupby <- water %>%
group_by(Borough,Revenue.Month) %>%
summarise_at(vars(Consumption..HCF.), list(name = mean))
gas_groupby <- gas %>%
group_by(Borough,Revenue.Month) %>%
summarise_at(vars(Consumption..Therms.), list(name = mean))
# combine energy dataset ----
electric_groupby$Energy <- 'Electric'
water_groupby$Energy <- 'Water'
gas_groupby$Energy <- 'Heating.Gas'
gas_groupby <- gas_groupby[gas_groupby$Revenue.Month!='2022-04',]
energy <- rbind(electric_groupby,water_groupby,gas_groupby)
colnames(energy)[3] <- 'Avg.Consumption'
energy$Revenue.Month <- anytime::anydate(energy$Revenue.Month) # convert to Date
Covid dataset
# clean & edit covid datset ----
covid <- covid[,c("date_of_interest", "BX_CASE_COUNT", "BK_CASE_COUNT", "MN_CASE_COUNT","QN_CASE_COUNT","SI_CASE_COUNT")]
covid$date_of_interest <- anytime::anydate(covid$date_of_interest) # convert type to Date
# sum by month
covid <- setDT(covid)[, lapply(.SD, sum), by = lubridate::floor_date(date_of_interest, "month")]
BRONX <- covid[,c('lubridate','BX_CASE_COUNT')] %>% rename('covid_case_count' = 'BX_CASE_COUNT') %>% mutate('Borough' = 'BRONX')
BROOKLYN <- covid[,c('lubridate','BK_CASE_COUNT')] %>% rename('covid_case_count' = 'BK_CASE_COUNT') %>% mutate('Borough' = 'BROOKLYN')
MANHATTAN <- covid[,c('lubridate','MN_CASE_COUNT')] %>% rename('covid_case_count' = 'MN_CASE_COUNT') %>% mutate('Borough' = 'MANHATTAN')
QUEENS <- covid[,c('lubridate','QN_CASE_COUNT')] %>% rename('covid_case_count' = 'QN_CASE_COUNT') %>% mutate('Borough' = 'QUEENS')
STATEN_ISLAND <- covid[,c('lubridate','SI_CASE_COUNT')] %>% rename('covid_case_count' = 'SI_CASE_COUNT') %>% mutate('Borough' = 'STATEN ISLAND')
# combine all boroughs
covid <- do.call("rbind", list(BRONX, BROOKLYN, MANHATTAN, QUEENS, STATEN_ISLAND))
Location Info.
# combine location info.----
data <- data %>% rename('Borough' = 'boroname')
data$Borough <- toupper(data$Borough)
test <- merge(energy, data, by='Borough')
test <- na.omit(test)
# combine covid ----
test <- merge(test, covid, by.x=c("Borough", "Revenue.Month"), by.y=c("Borough", "lubridate"))
# export final dataset for future use
# saveRDS(test, "final_dataset.rds")
class(test)
[1] "data.frame"
head(test)
Plots
Energy map plot
# map plot ----
# if not set energy type, go with the last value fo that day by borough
select_date = '2021-02-01'
select_energy = 'Water'
map_data = test[test$Revenue.Month == select_date & test$Energy == select_energy, c('Borough', 'Revenue.Month','Energy','Avg.Consumption','geometry')]
map_data <- st_as_sf(map_data,crs = st_crs(4326)) # convert data type for map plot
Warning: st_crs<- : replacing crs does not reproject data; use st_transform for that
pal <- colorBin("YlOrRd",5, domain = map_data$Avg.Consumption ) # max = 9, set 5 for 5 boroughs in nyc
labels <- sprintf(
"<strong>%s</strong><br/>%g",
map_data$Borough, map_data$Avg.Consumption) %>%
lapply(htmltools::HTML)
map_energy <- leaflet(map_data) %>%
setView(lng = -73.97, lat = 40.78, zoom = 10) %>%
addProviderTiles(provider = 'CartoDB.Positron') %>%
addPolygons(
label = labels,
color = "white",
dashArray = "3",
smoothFactor = 0.5,
opacity = 1,
fillOpacity = 0.7,
fillColor = ~pal(Avg.Consumption),
highlightOptions = highlightOptions(weight = 5,
fillOpacity = 0.7,
color = "#666",
opacity = 1,
bringToFront = TRUE)) %>%
addLegend(pal = pal,
values = ~Avg.Consumption,
title = 'Avg.Consumption',
opacity = 0.7,
position = "bottomright")
map_energy
# htmlwidgets::saveWidget(map_energy, file="../figs/map_energy.html") # save plot
Covid barplot
bar_data <- test[test$Revenue.Month == select_date, c('Borough', 'Revenue.Month' ,'covid_case_count')]
bar_covid <- ggplot(data = bar_data, aes(x = reorder(Borough, -covid_case_count), y = covid_case_count)) +
geom_bar(aes(fill = covid_case_count), stat = "identity") +
scale_fill_gradient(low = "yellow", high = '#DC143C' ) +
geom_text(aes(label = covid_case_count), color = "black", vjust = 1.6, size = 4) +
ggtitle("Covid case count by borough") +
labs(x = "Borough", y = "Covid case count") +
theme(legend.position="bottom") +
theme_minimal()
bar_covid

# ggsave("../figs/bar_covid.png") # save plot
Shiny app
test <- readRDS("final_dataset.rds")
# ui ----
ui <- fluidPage(
# app title ----
titlePanel("Energy Consumption vs. Covid Trend by Borough in NYC"),
sidebarLayout(
sidebarPanel(
h5('Based on the Census 2020, the popolation for the 5 boroughs of NYC
(Bronx, Brooklyn, Manhattan, Queens, Staten Island)
respectively are 1472654, 2736074, 1694263, 2405464, 495747 ;
and the density of population (person/km^2) are 13482, 15227, 28872, 8542, 3327.'),
br(),
# Input: Select for the borough ----
selectInput(inputId = "Energy",
label = "Choose an energy type:",
choices = c("Water", "Electric", "Heating Gas")),
# Input: Select for the enegry type ----
selectInput(inputId = "date",
label = "Choose an a date (by month):",
choices = unique(test$Revenue.Month))
),
# Main panel for displaying output ----
mainPanel(
leafletOutput(outputId = "mapPlot"),
br(),
h3("Covid case count by borough"),
plotOutput(outputId = "barPlot"),
br()
)
)
)
# server ----
server <- function(input, output) {
# modified dataset ----
Locations_data <- reactive({
# energy & date selection ----
## water ----
if ("Water" %in% input$Energy) {
return(test %>% filter(Revenue.Month == input$date & Energy == 'Water'))
}
## electric ----
if ("Electric" %in% input$Energy) {
return(test %>% filter(Revenue.Month == input$date & Energy == 'Electric'))
}
## heating&gas ----
if ( "Heating Gas" %in% input$Energy) {
return(test %>% filter(Revenue.Month == input$date & Energy == 'Heating.Gas'))
}
})
# mapPlot ----
output$mapPlot <-renderLeaflet({
dat = Locations_data()
dat <- st_as_sf(dat, crs = st_crs(4326)) # convert data object
pal <- colorBin("YlOrRd", 5, domain = dat$Avg.Consumption )
labels <- sprintf("<strong>%s</strong><br/>%g", dat$Borough, dat$Avg.Consumption) %>%
lapply(htmltools::HTML)
map_energy <- leaflet(dat) %>%
setView(lng = -73.97, lat = 40.78, zoom = 10) %>%
addProviderTiles(provider = 'CartoDB.Positron') %>%
addPolygons(
label = labels,
color = "white",
dashArray = "3",
smoothFactor = 0.5,
opacity = 1,
fillOpacity = 0.7,
fillColor = ~pal(Avg.Consumption),
highlightOptions = highlightOptions(weight = 5,
fillOpacity = 0.7,
color = "#666",
opacity = 1,
bringToFront = TRUE)) %>%
addLegend(pal = pal,
values = ~Avg.Consumption,
title = 'Avg.Consumption',
opacity = 0.7,
position = "bottomright")
map_energy
})
# barPlot ----
output$barPlot <-renderPlot({
dat = Locations_data()
bar_covid <- ggplot(data = dat, aes(x = reorder(Borough, -covid_case_count), y = covid_case_count)) +
geom_bar(aes(fill = covid_case_count), stat = "identity") +
scale_fill_gradient(low = "yellow", high = '#DC143C' ) +
geom_text(aes(label = covid_case_count), color = "black", vjust = 1.6, size = 8) +
labs(x = "Borough", y = "Covid case count") +
theme(legend.position="bottom") +
theme_minimal()
bar_covid
})
}
shinyApp(ui, server)
Listening on http://127.0.0.1:5746
Warning: st_crs<- : replacing crs does not reproject data; use st_transform for that
NA
LS0tCnRpdGxlOiAiUHJvamVjdDIiCmF1dGhvcjogIll1ZGFuIFpoYW5nIgpkYXRlOiAiMTAvNy8yMDIyIgpydW50aW1lOiBzaGlueQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiMgU2V0IHVwIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBsb2FkIHBhY2thZ2VzIC0tLS0KbGlicmFyeShzaGlueSkgIyBmb3Igc2hpbnkgYXBwCiMgZm9yIGVkaXQgZGF0YSBzZXQKbGlicmFyeSh0aWR5dmVyc2UpIApsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGRhdGEudGFibGUpCiMgZm9yIG1hcF9wbG90ICYgYmFyX3Bsb3QKbGlicmFyeShzZikKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGltcG9ydCBkYXRhc2V0IC0tLS0Kc2V0d2QoIi9Vc2Vycy96aGFuZ3l1ZGFuL0RvY3VtZW50cy9HaXRodWIvZmFsbDIwMjItcHJvamVjdDItZ3JvdXAxMi9kYXRhIikKZWxlY3RyaWMgPC0gcmVhZC5jc3YoJ0VsZWN0cmljX0NvbnN1bXB0aW9uX0FuZF9Db3N0X18yMDEwXy1fRmViXzIwMjJfLmNzdicpCndhdGVyIDwtIHJlYWQuY3N2KCdXYXRlcl9Db25zdW1wdGlvbl9BbmRfQ29zdF9fMjAxM18tX0ZlYl8yMDIyXy5jc3YnKQpnYXMgPC0gcmVhZC5jc3YoJ0hlYXRpbmdfR2FzX0NvbnN1bXB0aW9uX0FuZF9Db3N0X18yMDEwXy1fX0ZlYl8yMDIyXy5jc3YnKQpjb3ZpZCA8LSByZWFkLmNzdignY2FzZXMtYnktZGF5LmNzdicpCmRhdGEgPC0gc3RfcmVhZCgiQm9yb3VnaF9Cb3VuZGFyaWVzL2dlb19leHBvcnRfYTU5YTdjMDctYmQ3MC00YjExLWFmOWYtMDExYjVhZDJhOTYzLnNocCIpCmBgYAoKIyBDbGVhbiAmIEVkaXQgCgojIyBFbmVyZ3kgZGF0YXNldCAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBzZWxlY3QgY29scyAtLS0tCmVsZWN0cmljIDwtIGVsZWN0cmljWyxjKCdCb3JvdWdoJywnUmV2ZW51ZS5Nb250aCcsJ0NvbnN1bXB0aW9uLi5LV0guJyldCndhdGVyIDwtIHdhdGVyWyxjKCdCb3JvdWdoJywnUmV2ZW51ZS5Nb250aCcsJ0NvbnN1bXB0aW9uLi5IQ0YuJyldCmdhcyA8LSBnYXNbLGMoJ0Jvcm91Z2gnLCdSZXZlbnVlLk1vbnRoJywnQ29uc3VtcHRpb24uLlRoZXJtcy4nKV0KCiMgZmlsdGVyIGRhdGEgYnkgZGF0ZSAtLS0tCmVsZWN0cmljIDwtIHN1YnNldChlbGVjdHJpYywgUmV2ZW51ZS5Nb250aCA+IDIwMTkpCndhdGVyIDwtIHN1YnNldCh3YXRlciwgUmV2ZW51ZS5Nb250aCA+IDIwMTkpCmdhcyA8LSBzdWJzZXQoZ2FzLCBSZXZlbnVlLk1vbnRoID4gMjAxOSkKCiMgZ3JvdXAgYnkgQm9yb3VnaCAmIGRhdGUgdG8gZ2V0IG1lYW4oQ29uc3VtcHRpb24uLktXSC4pIC0tLS0KZWxlY3RyaWNfZ3JvdXBieSA8LSBlbGVjdHJpYyAlPiUgCiAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieShCb3JvdWdoLFJldmVudWUuTW9udGgpICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZV9hdCh2YXJzKENvbnN1bXB0aW9uLi5LV0guKSwgbGlzdChuYW1lID0gbWVhbikpCgp3YXRlcl9ncm91cGJ5IDwtIHdhdGVyICU+JSAKICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoQm9yb3VnaCxSZXZlbnVlLk1vbnRoKSAlPiUgCiAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZV9hdCh2YXJzKENvbnN1bXB0aW9uLi5IQ0YuKSwgbGlzdChuYW1lID0gbWVhbikpCgpnYXNfZ3JvdXBieSA8LSBnYXMgJT4lIAogICAgICAgICAgICAgICAgZ3JvdXBfYnkoQm9yb3VnaCxSZXZlbnVlLk1vbnRoKSAlPiUgCiAgICAgICAgICAgICAgICBzdW1tYXJpc2VfYXQodmFycyhDb25zdW1wdGlvbi4uVGhlcm1zLiksIGxpc3QobmFtZSA9IG1lYW4pKQoKIyBjb21iaW5lIGVuZXJneSBkYXRhc2V0IC0tLS0KZWxlY3RyaWNfZ3JvdXBieSRFbmVyZ3kgPC0gICdFbGVjdHJpYycKd2F0ZXJfZ3JvdXBieSRFbmVyZ3kgPC0gJ1dhdGVyJwpnYXNfZ3JvdXBieSRFbmVyZ3kgPC0gJ0hlYXRpbmcuR2FzJwpnYXNfZ3JvdXBieSA8LSBnYXNfZ3JvdXBieVtnYXNfZ3JvdXBieSRSZXZlbnVlLk1vbnRoIT0nMjAyMi0wNCcsXQplbmVyZ3kgPC0gcmJpbmQoZWxlY3RyaWNfZ3JvdXBieSx3YXRlcl9ncm91cGJ5LGdhc19ncm91cGJ5KQpjb2xuYW1lcyhlbmVyZ3kpWzNdIDwtICdBdmcuQ29uc3VtcHRpb24nCmVuZXJneSRSZXZlbnVlLk1vbnRoIDwtIGFueXRpbWU6OmFueWRhdGUoZW5lcmd5JFJldmVudWUuTW9udGgpICMgY29udmVydCB0byBEYXRlCmBgYAoKIyMgQ292aWQgZGF0YXNldCAKYGBge3J9CiMgY2xlYW4gJiBlZGl0IGNvdmlkIGRhdHNldCAtLS0tCmNvdmlkIDwtIGNvdmlkWyxjKCJkYXRlX29mX2ludGVyZXN0IiwgIkJYX0NBU0VfQ09VTlQiLCAiQktfQ0FTRV9DT1VOVCIsICJNTl9DQVNFX0NPVU5UIiwiUU5fQ0FTRV9DT1VOVCIsIlNJX0NBU0VfQ09VTlQiKV0KCmNvdmlkJGRhdGVfb2ZfaW50ZXJlc3QgPC0gYW55dGltZTo6YW55ZGF0ZShjb3ZpZCRkYXRlX29mX2ludGVyZXN0KSAjIGNvbnZlcnQgdHlwZSB0byBEYXRlCgojIHN1bSBieSBtb250aCAKY292aWQgPC0gc2V0RFQoY292aWQpWywgbGFwcGx5KC5TRCwgc3VtKSwgYnkgPSBsdWJyaWRhdGU6OmZsb29yX2RhdGUoZGF0ZV9vZl9pbnRlcmVzdCwgIm1vbnRoIildCgpCUk9OWCA8LSBjb3ZpZFssYygnbHVicmlkYXRlJywnQlhfQ0FTRV9DT1VOVCcpXSAlPiUgcmVuYW1lKCdjb3ZpZF9jYXNlX2NvdW50JyA9ICdCWF9DQVNFX0NPVU5UJykgJT4lIG11dGF0ZSgnQm9yb3VnaCcgPSAnQlJPTlgnKQpCUk9PS0xZTiA8LSBjb3ZpZFssYygnbHVicmlkYXRlJywnQktfQ0FTRV9DT1VOVCcpXSAlPiUgcmVuYW1lKCdjb3ZpZF9jYXNlX2NvdW50JyA9ICdCS19DQVNFX0NPVU5UJykgJT4lIG11dGF0ZSgnQm9yb3VnaCcgPSAnQlJPT0tMWU4nKQpNQU5IQVRUQU4gPC0gY292aWRbLGMoJ2x1YnJpZGF0ZScsJ01OX0NBU0VfQ09VTlQnKV0gJT4lIHJlbmFtZSgnY292aWRfY2FzZV9jb3VudCcgPSAnTU5fQ0FTRV9DT1VOVCcpICU+JSBtdXRhdGUoJ0Jvcm91Z2gnID0gJ01BTkhBVFRBTicpClFVRUVOUyA8LSBjb3ZpZFssYygnbHVicmlkYXRlJywnUU5fQ0FTRV9DT1VOVCcpXSAlPiUgcmVuYW1lKCdjb3ZpZF9jYXNlX2NvdW50JyA9ICdRTl9DQVNFX0NPVU5UJykgJT4lIG11dGF0ZSgnQm9yb3VnaCcgPSAnUVVFRU5TJykKU1RBVEVOX0lTTEFORCA8LSBjb3ZpZFssYygnbHVicmlkYXRlJywnU0lfQ0FTRV9DT1VOVCcpXSAlPiUgcmVuYW1lKCdjb3ZpZF9jYXNlX2NvdW50JyA9ICdTSV9DQVNFX0NPVU5UJykgJT4lIG11dGF0ZSgnQm9yb3VnaCcgPSAnU1RBVEVOIElTTEFORCcpCgojIGNvbWJpbmUgYWxsIGJvcm91Z2hzIApjb3ZpZCA8LSBkby5jYWxsKCJyYmluZCIsIGxpc3QoQlJPTlgsIEJST09LTFlOLCBNQU5IQVRUQU4sIFFVRUVOUywgU1RBVEVOX0lTTEFORCkpCmBgYAoKIyMgTG9jYXRpb24gSW5mby4KYGBge3J9CiMgY29tYmluZSBsb2NhdGlvbiBpbmZvLi0tLS0KZGF0YSA8LSBkYXRhICU+JSByZW5hbWUoJ0Jvcm91Z2gnID0gJ2Jvcm9uYW1lJykKZGF0YSRCb3JvdWdoIDwtICB0b3VwcGVyKGRhdGEkQm9yb3VnaCkKdGVzdCA8LSBtZXJnZShlbmVyZ3ksIGRhdGEsIGJ5PSdCb3JvdWdoJykgCnRlc3QgPC0gbmEub21pdCh0ZXN0KQoKIyBjb21iaW5lIGNvdmlkIC0tLS0KdGVzdCA8LSBtZXJnZSh0ZXN0LCBjb3ZpZCwgYnkueD1jKCJCb3JvdWdoIiwgIlJldmVudWUuTW9udGgiKSwgYnkueT1jKCJCb3JvdWdoIiwgImx1YnJpZGF0ZSIpKQojIGV4cG9ydCBmaW5hbCBkYXRhc2V0IGZvciBmdXR1cmUgdXNlCiMgc2F2ZVJEUyh0ZXN0LCAiZmluYWxfZGF0YXNldC5yZHMiKQpjbGFzcyh0ZXN0KQpoZWFkKHRlc3QpCmBgYAoKIyBQbG90cwoKIyMgRW5lcmd5IG1hcCBwbG90IApgYGB7cn0KIyBtYXAgcGxvdCAtLS0tCiMgaWYgbm90IHNldCBlbmVyZ3kgdHlwZSwgZ28gd2l0aCB0aGUgbGFzdCB2YWx1ZSBmbyB0aGF0IGRheSBieSBib3JvdWdoCnNlbGVjdF9kYXRlID0gJzIwMjEtMDItMDEnCnNlbGVjdF9lbmVyZ3kgPSAnV2F0ZXInCgptYXBfZGF0YSA9IHRlc3RbdGVzdCRSZXZlbnVlLk1vbnRoID09IHNlbGVjdF9kYXRlICYgdGVzdCRFbmVyZ3kgPT0gc2VsZWN0X2VuZXJneSwgYygnQm9yb3VnaCcsICdSZXZlbnVlLk1vbnRoJywnRW5lcmd5JywnQXZnLkNvbnN1bXB0aW9uJywnZ2VvbWV0cnknKV0KCm1hcF9kYXRhIDwtIHN0X2FzX3NmKG1hcF9kYXRhLGNycyA9IHN0X2Nycyg0MzI2KSkgIyBjb252ZXJ0IGRhdGEgdHlwZSBmb3IgbWFwIHBsb3QKCnBhbCA8LSBjb2xvckJpbigiWWxPclJkIiw1LCBkb21haW4gPSBtYXBfZGF0YSRBdmcuQ29uc3VtcHRpb24gKSAjIG1heCA9IDksIHNldCA1IGZvciA1IGJvcm91Z2hzIGluIG55YwoKbGFiZWxzIDwtIHNwcmludGYoCiAgIjxzdHJvbmc+JXM8L3N0cm9uZz48YnIvPiVnIiwKICBtYXBfZGF0YSRCb3JvdWdoLCBtYXBfZGF0YSRBdmcuQ29uc3VtcHRpb24pICU+JSAKICBsYXBwbHkoaHRtbHRvb2xzOjpIVE1MKQoKbWFwX2VuZXJneSA8LSBsZWFmbGV0KG1hcF9kYXRhKSAlPiUKICBzZXRWaWV3KGxuZyA9IC03My45NywgbGF0ID0gNDAuNzgsIHpvb20gPSAxMCkgJT4lCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlciA9ICdDYXJ0b0RCLlBvc2l0cm9uJykgJT4lCiAgYWRkUG9seWdvbnMoCiAgICBsYWJlbCA9IGxhYmVscywKICAgIGNvbG9yID0gIndoaXRlIiwKICAgIGRhc2hBcnJheSA9ICIzIiwKICAgIHNtb290aEZhY3RvciA9IDAuNSwKICAgIG9wYWNpdHkgPSAxLAogICAgZmlsbE9wYWNpdHkgPSAwLjcsCiAgICBmaWxsQ29sb3IgPSB+cGFsKEF2Zy5Db25zdW1wdGlvbiksCiAgICBoaWdobGlnaHRPcHRpb25zID0gaGlnaGxpZ2h0T3B0aW9ucyh3ZWlnaHQgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjNjY2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJpbmdUb0Zyb250ID0gVFJVRSkpICU+JQogIGFkZExlZ2VuZChwYWwgPSBwYWwsIAogICAgICAgICAgICB2YWx1ZXMgPSB+QXZnLkNvbnN1bXB0aW9uLCAKICAgICAgICAgICAgdGl0bGUgPSAnQXZnLkNvbnN1bXB0aW9uJywKICAgICAgICAgICAgb3BhY2l0eSA9IDAuNywgCiAgICAgICAgICAgIHBvc2l0aW9uID0gImJvdHRvbXJpZ2h0IikKbWFwX2VuZXJneQoKIyBodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChtYXBfZW5lcmd5LCBmaWxlPSIuLi9maWdzL21hcF9lbmVyZ3kuaHRtbCIpICMgc2F2ZSBwbG90CmBgYAoKCiMjIENvdmlkIGJhcnBsb3QKYGBge3IgZWNobz1UUlVFfQpiYXJfZGF0YSA8LSB0ZXN0W3Rlc3QkUmV2ZW51ZS5Nb250aCA9PSBzZWxlY3RfZGF0ZSwgYygnQm9yb3VnaCcsICdSZXZlbnVlLk1vbnRoJyAsJ2NvdmlkX2Nhc2VfY291bnQnKV0KCmJhcl9jb3ZpZCA8LSBnZ3Bsb3QoZGF0YSA9IGJhcl9kYXRhLCBhZXMoeCA9IHJlb3JkZXIoQm9yb3VnaCwgLWNvdmlkX2Nhc2VfY291bnQpLCB5ID0gY292aWRfY2FzZV9jb3VudCkpICsKICBnZW9tX2JhcihhZXMoZmlsbCA9IGNvdmlkX2Nhc2VfY291bnQpLCBzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInllbGxvdyIsIGhpZ2ggPSAnI0RDMTQzQycgKSArIAogIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb3ZpZF9jYXNlX2NvdW50KSwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNiwgc2l6ZSA9IDQpICsKICBnZ3RpdGxlKCJDb3ZpZCBjYXNlIGNvdW50IGJ5IGJvcm91Z2giKSArIAogIGxhYnMoeCA9ICJCb3JvdWdoIiwgeSA9ICJDb3ZpZCBjYXNlIGNvdW50IikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsKICB0aGVtZV9taW5pbWFsKCkKCmJhcl9jb3ZpZAojIGdnc2F2ZSgiLi4vZmlncy9iYXJfY292aWQucG5nIikgIyBzYXZlIHBsb3QKYGBgCgojIFNoaW55IGFwcApgYGB7cn0KdGVzdCA8LSByZWFkUkRTKCJmaW5hbF9kYXRhc2V0LnJkcyIpCiMgdWkgLS0tLQp1aSA8LSBmbHVpZFBhZ2UoCiAgIyBhcHAgdGl0bGUgLS0tLQogIHRpdGxlUGFuZWwoIkVuZXJneSBDb25zdW1wdGlvbiB2cy4gQ292aWQgVHJlbmQgYnkgQm9yb3VnaCBpbiBOWUMiKSwKICBzaWRlYmFyTGF5b3V0KAogICAgc2lkZWJhclBhbmVsKAogICAgICBoNSgnQmFzZWQgb24gdGhlIENlbnN1cyAyMDIwLCB0aGUgcG9wb2xhdGlvbiBmb3IgdGhlIDUgYm9yb3VnaHMgb2YgTllDIAogICAgICAoQnJvbngsIEJyb29rbHluLCBNYW5oYXR0YW4sIFF1ZWVucywgU3RhdGVuIElzbGFuZCkKICAgICAgcmVzcGVjdGl2ZWx5IGFyZSAxNDcyNjU0LCAyNzM2MDc0LCAxNjk0MjYzLCAyNDA1NDY0LCA0OTU3NDcgOyAKICAgICAgYW5kIHRoZSBkZW5zaXR5IG9mIHBvcHVsYXRpb24gKHBlcnNvbi9rbV4yKSBhcmUgMTM0ODIsIDE1MjI3LCAyODg3MiwgODU0MiwgMzMyNy4nKSwKICAgICAgYnIoKSwKICAgICAgIyBJbnB1dDogU2VsZWN0IGZvciB0aGUgYm9yb3VnaCAtLS0tCiAgICAgIHNlbGVjdElucHV0KGlucHV0SWQgPSAiRW5lcmd5IiwKICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiQ2hvb3NlIGFuIGVuZXJneSB0eXBlOiIsCiAgICAgICAgICAgICAgICAgIGNob2ljZXMgPSBjKCJXYXRlciIsICJFbGVjdHJpYyIsICJIZWF0aW5nIEdhcyIpKSwKICAgICAgCiAgICAgICMgSW5wdXQ6IFNlbGVjdCBmb3IgdGhlIGVuZWdyeSB0eXBlIC0tLS0KICAgICAgc2VsZWN0SW5wdXQoaW5wdXRJZCA9ICJkYXRlIiwKICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiQ2hvb3NlIGFuIGEgZGF0ZSAoYnkgbW9udGgpOiIsCiAgICAgICAgICAgICAgICAgIGNob2ljZXMgPSB1bmlxdWUodGVzdCRSZXZlbnVlLk1vbnRoKSkKICAgICksCiAgICAjIE1haW4gcGFuZWwgZm9yIGRpc3BsYXlpbmcgb3V0cHV0IC0tLS0KICAgIG1haW5QYW5lbCgKICAgICAgbGVhZmxldE91dHB1dChvdXRwdXRJZCA9ICJtYXBQbG90IiksCiAgICAgIGJyKCksCiAgICAgIGgzKCJDb3ZpZCBjYXNlIGNvdW50IGJ5IGJvcm91Z2giKSwKICAgICAgcGxvdE91dHB1dChvdXRwdXRJZCA9ICJiYXJQbG90IiksCiAgICAgIGJyKCkKICAgICkKICApCikKCgojIHNlcnZlciAtLS0tCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7CiAgCiMgbW9kaWZpZWQgZGF0YXNldCAtLS0tCiAgTG9jYXRpb25zX2RhdGEgPC0gcmVhY3RpdmUoewogIAogICAgIyBlbmVyZ3kgJiBkYXRlIHNlbGVjdGlvbiAtLS0tCiAgICAjIyAgd2F0ZXIgLS0tLQogICAgaWYgKCJXYXRlciIgJWluJSBpbnB1dCRFbmVyZ3kpIHsKICAgICAgcmV0dXJuKHRlc3QgJT4lIGZpbHRlcihSZXZlbnVlLk1vbnRoID09IGlucHV0JGRhdGUgJiBFbmVyZ3kgPT0gJ1dhdGVyJykpCiAgICB9CgogICAgIyMgZWxlY3RyaWMgLS0tLQogICAgaWYgKCJFbGVjdHJpYyIgJWluJSBpbnB1dCRFbmVyZ3kpIHsKICAgICAgcmV0dXJuKHRlc3QgJT4lIGZpbHRlcihSZXZlbnVlLk1vbnRoID09IGlucHV0JGRhdGUgJiBFbmVyZ3kgPT0gJ0VsZWN0cmljJykpCiAgICB9CiAgICAKICAgICMjIGhlYXRpbmcmZ2FzIC0tLS0KICAgIGlmICggIkhlYXRpbmcgR2FzIiAlaW4lIGlucHV0JEVuZXJneSkgewogICAgICByZXR1cm4odGVzdCAlPiUgZmlsdGVyKFJldmVudWUuTW9udGggPT0gaW5wdXQkZGF0ZSAmIEVuZXJneSA9PSAnSGVhdGluZy5HYXMnKSkKICAgIH0KICB9KQogIAogICAgIyBtYXBQbG90IC0tLS0KICBvdXRwdXQkbWFwUGxvdCA8LXJlbmRlckxlYWZsZXQoewogIAogICAgZGF0ID0gTG9jYXRpb25zX2RhdGEoKQogICAgZGF0IDwtIHN0X2FzX3NmKGRhdCwgY3JzID0gc3RfY3JzKDQzMjYpKSAjIGNvbnZlcnQgZGF0YSBvYmplY3QgCiAgICAKICAgIHBhbCA8LSBjb2xvckJpbigiWWxPclJkIiwgNSwgZG9tYWluID0gZGF0JEF2Zy5Db25zdW1wdGlvbiApCiAgICBsYWJlbHMgPC0gc3ByaW50ZigiPHN0cm9uZz4lczwvc3Ryb25nPjxici8+JWciLCBkYXQkQm9yb3VnaCwgZGF0JEF2Zy5Db25zdW1wdGlvbikgJT4lIAogICAgICBsYXBwbHkoaHRtbHRvb2xzOjpIVE1MKQogICAgCiAgICBtYXBfZW5lcmd5IDwtIGxlYWZsZXQoZGF0KSAlPiUKICBzZXRWaWV3KGxuZyA9IC03My45NywgbGF0ID0gNDAuNzgsIHpvb20gPSAxMCkgJT4lCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlciA9ICdDYXJ0b0RCLlBvc2l0cm9uJykgJT4lCiAgYWRkUG9seWdvbnMoCiAgICBsYWJlbCA9IGxhYmVscywKICAgIGNvbG9yID0gIndoaXRlIiwKICAgIGRhc2hBcnJheSA9ICIzIiwKICAgIHNtb290aEZhY3RvciA9IDAuNSwKICAgIG9wYWNpdHkgPSAxLAogICAgZmlsbE9wYWNpdHkgPSAwLjcsCiAgICBmaWxsQ29sb3IgPSB+cGFsKEF2Zy5Db25zdW1wdGlvbiksCiAgICBoaWdobGlnaHRPcHRpb25zID0gaGlnaGxpZ2h0T3B0aW9ucyh3ZWlnaHQgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjNjY2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJpbmdUb0Zyb250ID0gVFJVRSkpICU+JQogIGFkZExlZ2VuZChwYWwgPSBwYWwsIAogICAgICAgICAgICB2YWx1ZXMgPSB+QXZnLkNvbnN1bXB0aW9uLCAKICAgICAgICAgICAgdGl0bGUgPSAnQXZnLkNvbnN1bXB0aW9uJywKICAgICAgICAgICAgb3BhY2l0eSA9IDAuNywgCiAgICAgICAgICAgIHBvc2l0aW9uID0gImJvdHRvbXJpZ2h0IikKbWFwX2VuZXJneQogIH0pCiAgCiAgIyBiYXJQbG90IC0tLS0KICAgb3V0cHV0JGJhclBsb3QgPC1yZW5kZXJQbG90KHsKICAgICBkYXQgPSBMb2NhdGlvbnNfZGF0YSgpCiAgICAgYmFyX2NvdmlkIDwtIGdncGxvdChkYXRhID0gZGF0LCBhZXMoeCA9IHJlb3JkZXIoQm9yb3VnaCwgLWNvdmlkX2Nhc2VfY291bnQpLCB5ID0gY292aWRfY2FzZV9jb3VudCkpICsKICBnZW9tX2JhcihhZXMoZmlsbCA9IGNvdmlkX2Nhc2VfY291bnQpLCBzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInllbGxvdyIsIGhpZ2ggPSAnI0RDMTQzQycgKSArIAogIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb3ZpZF9jYXNlX2NvdW50KSwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNiwgc2l6ZSA9IDgpICsKICBsYWJzKHggPSAiQm9yb3VnaCIsIHkgPSAiQ292aWQgY2FzZSBjb3VudCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArCiAgdGhlbWVfbWluaW1hbCgpCiAgICAgYmFyX2NvdmlkCiAgIH0pCn0KCnNoaW55QXBwKHVpLCBzZXJ2ZXIpCmBgYAo=